package com.hzbc.faceattendance.face.alarm;


import com.alibaba.fastjson.JSONObject;
import com.hzbc.faceattendance.face.HCNetSDK;
import com.hzbc.faceattendance.face.OSSelect;
import com.hzbc.faceattendance.handler.SpringContextHolder;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class Alarm implements DisposableBean {

    public static HCNetSDK hCNetSDK = null;
    static int lUserID = -1;//用户句柄 实现对设备登录
    static int lAlarmHandle = -1;//报警布防句柄
//    static int lListenHandle = -1;//报警监听句柄
    static FMSGCallBack_V31 fMSFCallBack_V31 = null; //报警布防回调函数
//    static FMSGCallBack fMSFCallBack = null; //报警监听回调函数
    private volatile boolean running = true;
    private Thread monitorThread;

    public void setlUserID(int lUserID) {this.lUserID = lUserID;}
    public int getlUserID(){return lUserID;}
    /**
     * 动态库加载
     *
     * @return
     */
    private static boolean createSDKInstance() {
        if (hCNetSDK == null) {
            synchronized (HCNetSDK.class) {
                String strDllPath = "";
                try {
                    if (OSSelect.isWindows())
                        //win系统加载库路径
                        strDllPath = System.getProperty("user.dir") + "\\lib\\HCNetSDK.dll";
                    else if (OSSelect.isLinux())
                        //Linux系统加载库路径
                        strDllPath = System.getProperty("user.dir") + "/lib/libhcnetsdk.so";
                    hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);
                } catch (Exception ex) {
                    System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 登录设备，支持 V40 和 V30 版本，功能一致。
     *
     * @param ip   设备IP地址
     * @param port SDK端口，默认为设备的8000端口
     * @param user 设备用户名
     * @param psw  设备密码
     * @return 登录成功返回用户ID，失败返回-1
     */
    public static int loginDevice(String ip, short port, String user, String psw) {
        // 创建设备登录信息和设备信息对象
        HCNetSDK.NET_DVR_USER_LOGIN_INFO loginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
        HCNetSDK.NET_DVR_DEVICEINFO_V40 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();

        // 设置设备IP地址
        byte[] deviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        byte[] ipBytes = ip.getBytes();
        System.arraycopy(ipBytes, 0, deviceAddress, 0, Math.min(ipBytes.length, deviceAddress.length));
        loginInfo.sDeviceAddress = deviceAddress;

        // 设置用户名和密码
        byte[] userName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        byte[] password = psw.getBytes();
        System.arraycopy(user.getBytes(), 0, userName, 0, Math.min(user.length(), userName.length));
        System.arraycopy(password, 0, loginInfo.sPassword, 0, Math.min(password.length, loginInfo.sPassword.length));
        loginInfo.sUserName = userName;

        // 设置端口和登录模式
        loginInfo.wPort = port;
        loginInfo.bUseAsynLogin = false; // 同步登录
        loginInfo.byLoginMode = 0; // 使用SDK私有协议

        // 执行登录操作
        int userID = hCNetSDK.NET_DVR_Login_V40(loginInfo, deviceInfo);
        if (userID == -1) {
            System.err.println("登录失败，错误码为: " + hCNetSDK.NET_DVR_GetLastError());
        } else {
            System.out.println(ip + " 设备登录成功！");
            // 处理通道号逻辑
            int startDChan = deviceInfo.struDeviceV30.byStartDChan;
            System.out.println("预览起始通道号: " + startDChan);
        }
        return userID; // 返回登录结果
    }

    /**
     * 报警布防
     *
     * @param userID 设备登录句柄ID
     * @return
     */
    public static int setAlarmChan(int userID) {
        if (userID == -1) {
            System.out.println("请先注册");
            return -1;
        }
        if (lAlarmHandle < 0)//尚未布防,需要布防
        {
            //报警布防参数设置
            HCNetSDK.NET_DVR_SETUPALARM_PARAM_V50 alarmInfo = new HCNetSDK.NET_DVR_SETUPALARM_PARAM_V50();
            alarmInfo.dwSize = alarmInfo.size();
            alarmInfo.byLevel = 0;  //布防等级
//            alarmInfo.byAlarmInfoType = 1;   // 智能交通报警信息上传类型：0- 老报警信息（NET_DVR_PLATE_RESULT），1- 新报警信息(NET_ITS_PLATE_RESULT)
            alarmInfo.byFaceAlarmDetection = 1;   // 人脸报警信息类型：1- 人脸侦测报警(报警类型：COMM_ALARM_FACE_DETECTION，NET_DVR_FACE_DETECTION)，0- 人脸抓拍报警(报警类型：COMM_UPLOAD_FACESNAP_RESULT，NET_VCA_FACESNAP_RESULT)
            alarmInfo.byDeployType = 0;   //布防类型：0-客户端布防，1-实时布防，客户端布防仅支持一路
            alarmInfo.byAlarmTypeURL=0; //表示特征抓拍报警上传（INTER_FACESNAP_RESULT）；0-表示二进制传输，1-表示URL传输
            // 检查人脸比对报警是否设置为二进制传输
            boolean isBinary = (alarmInfo.byAlarmTypeURL & (1 << 2)) == 0;
            System.out.println("人脸比对报警使用二进制传输: " + isBinary);  // 应该输出 true
            alarmInfo.write();
            log.info("报警布防参数设置:{}", JSONObject.toJSONString(alarmInfo));
            lAlarmHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V50(userID, alarmInfo,Pointer.NULL,0);
            if (lAlarmHandle == -1) {
                System.err.println("布防失败，错误码为" + hCNetSDK.NET_DVR_GetLastError());
                return -1;
            } else {
                System.out.println("布防成功");
                return lAlarmHandle;
            }
        } else {
            System.out.println("设备已经布防，请先撤防！");
            return lAlarmHandle;
        }
    }

    /**
     * 设备注销
     *
     * @param
     */
    public static void logoutDev(int userID) {
        if (userID > -1) {
            if (hCNetSDK.NET_DVR_Logout(userID)) {
                System.out.println("注销成功");
            }
        } else {
            System.out.println("设备未注册，请先注册");
        }

    }

    @PostConstruct
    public void init() {
        monitorThread = new Thread(() -> {
            initializeSDK();
            startDeviceMonitoring();
        });
        monitorThread.setDaemon(false);
        monitorThread.start();
    }

    private void initializeSDK() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (hCNetSDK == null) {
            if (!createSDKInstance()) {
                System.out.println("Load SDK fail");
                return;
            }
        }
        //linux系统建议调用以下接口加载组件库
        if (OSSelect.isLinux()) {
            HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);
            HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);
            //这里是库的绝对路径，请根据实际情况修改，注意改路径必须有访问权限
            String strPath1 = System.getProperty("user.dir") + "/lib/libcrypto.so.1.1";
            String strPath2 = System.getProperty("user.dir") + "/lib/libssl.so.1.1";
            System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());
            ptrByteArray1.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_LIBEAY_PATH, ptrByteArray1.getPointer());
            System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());
            ptrByteArray2.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_SSLEAY_PATH, ptrByteArray2.getPointer());
            String strPathCom = System.getProperty("user.dir") + "/lib/";
            HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();
            System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());
            struComPath.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_SDK_PATH, struComPath.getPointer());
        }

        /**初始化*/
        hCNetSDK.NET_DVR_Init();
        /**加载日志*/
        hCNetSDK.NET_DVR_SetLogToFile(3, "./log", false);
        //设置报警回调函数
        if (fMSFCallBack_V31 == null) {
            // 使用Spring上下文获取已注入依赖的Bean
            fMSFCallBack_V31 = SpringContextHolder.getBean(FMSGCallBack_V31.class);
            Pointer pUser = null;
            if (!hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(fMSFCallBack_V31, pUser)) {
                System.out.println("设置回调函数失败!");
                return;
            } else {
                System.out.println("设置回调函数成功!");
            }
        }
        /** 设备上传的报警信息是COMM_VCA_ALARM(0x4993)类型，
         在SDK初始化之后增加调用NET_DVR_SetSDKLocalCfg(enumType为NET_DVR_LOCAL_CFG_TYPE_GENERAL)设置通用参数NET_DVR_LOCAL_GENERAL_CFG的byAlarmJsonPictureSeparate为1，
         将Json数据和图片数据分离上传，这样设置之后，报警布防回调函数里面接收到的报警信息类型为COMM_ISAPI_ALARM(0x6009)，
         报警信息结构体为NET_DVR_ALARM_ISAPI_INFO（与设备无关，SDK封装的数据结构），更便于解析。*/

        HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG struNET_DVR_LOCAL_GENERAL_CFG = new HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG();
        struNET_DVR_LOCAL_GENERAL_CFG.byAlarmJsonPictureSeparate = 1;   //设置JSON透传报警数据和图片分离
        struNET_DVR_LOCAL_GENERAL_CFG.write();
        Pointer pStrNET_DVR_LOCAL_GENERAL_CFG = struNET_DVR_LOCAL_GENERAL_CFG.getPointer();
        hCNetSDK.NET_DVR_SetSDKLocalCfg(17, pStrNET_DVR_LOCAL_GENERAL_CFG);

    }

    @PreDestroy
    @Override
    public void destroy() {
        running = false;
        if (monitorThread != null) {
            monitorThread.interrupt();
        }
        // 资源释放逻辑
        if (lUserID != -1) {
            logoutDev(lUserID);
        }
        hCNetSDK.NET_DVR_Cleanup();
    }

    private void startDeviceMonitoring() {
        lUserID = loginDevice("159.75.87.53", (short) 65107, "admin", "Agora20250730");
        lAlarmHandle = setAlarmChan(lUserID);
    }


}